#ifndef FUTURE_HPP
#define FUTURE_HPP

#include <future>
#include <iostream>
#include <memory>
#include <thread>

// ref: https://en.cppreference.com/w/cpp/thread/future

namespace future {
void _test1() {
  std::packaged_task<int()> task([]() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << __FUNCTION__ << std::endl;
    return 0;
  });
  std::future<int> ret = task.get_future();
  std::thread t([&ret, &task]() {
    std::cout << __FUNCTION__ << std::endl;
    task();
    ret.wait();
    std::cout << ret.get() << std::endl;
  });
  // std::thread t(std::move(task));
  t.join();
}
void test1() {
  _test1();
  std::cout << __FUNCTION__ << std::endl;
}

void test2() {
  // future from a packaged_task
  std::packaged_task<int()> task([] { return 7; }); // wrap the function
  std::future<int> f1 = task.get_future();          // get a future
  std::thread t(std::move(task));                   // launch on a thread

  // future from an async()
  std::future<int> f2 = std::async(std::launch::async, [] { return 8; });

  // future from a promise
  std::promise<int> p;
  std::future<int> f3 = p.get_future();
  std::thread([&p] { p.set_value_at_thread_exit(9); }).detach();

  std::cout << "Waiting..." << std::flush;
  f1.wait();
  f2.wait();
  f3.wait();
  std::cout << "Done!\nResults are: " << f1.get() << ' ' << f2.get() << ' '
            << f3.get() << '\n';
  t.join();
}

void test3() {
  std::future<void> ret =
      std::async([]() { std::cout << "async" << std::endl; });
  std::cout << __FUNCTION__ << std::endl;
  ret.wait();
}

void test4() {
  // typedef void(*function_t)(void);
  // function_t f = [](){
  //   std::cout << "async" << std::endl;
  // };
  // std::unique_ptr<function_t> f_ptr(new function_t(f));

  std::function<void(void)> f = []() { std::cout << "async" << std::endl; };
  std::function<void(void)> *pf = &f;
  // std::future<void> ret = std::future<void>(f);
  std::thread([pf]() { (*pf)(); }).detach();
}

} // namespace future

void future_demo() { future::test4(); }

#endif // FUTURE_HPP